﻿using Data.MongoDb.Entity;
using MongoDB.Bson;
using MongoDB.Driver.Linq;
using Data.MongoDb.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MongoDB.Driver;
using System.Reflection;

namespace Data.MongoDb
{
    public class MongoRepositoryWithTypedId<T, TId> : MongoBase, IRepositoryWithTypedId<T, TId> where T : EntityWithTypeId<TId>
    {
        /// <summary>
        /// MongoCollection field
        /// </summary>
        private readonly MongoCollectionWrapper<T, TId> _collection;

        /// <summary>
        /// Initilizes the instance of MongoRepository, Setups the MongoDB and The Collection (i.e T)
        /// Uses the Default App.Config tag names to fetch the connectionString and Database name
        /// Check the MongoSetting class for the App.Config tag names.
        /// </summary>
        public MongoRepositoryWithTypedId()
        {
            var db = Init();
            var collectionName = InferCollectionNameFrom();
            _collection = new MongoCollectionWrapper<T, TId>(db.GetCollection<T>(collectionName));
        }

        protected IMongoCollection<T> GetMongoCollection()
        {
            return _collection.GetMongoCollection();
        }

        private string InferCollectionNameFrom()
        {
            var type = typeof(T);
            return type.Name;
        }

        /// <summary>
        /// Gets the Mongo collection (to perform advance operations)
        /// </summary>
        protected internal MongoCollectionWrapper<T, TId> Collection
        {
            get { return _collection; }
        }

        #region IRepositoryWithTypedId<T,TId> Members

        public virtual T Get(TId id)
        {
            return Collection.FindOneById(id);
        }

        public virtual IEnumerable<T> Find(QueryDocument args)
        {
            return Collection.Find(args);
        }

        public void Insert(T entity)
        {
            Collection.Insert(entity);
        }

        public void InsertMany(IEnumerable<T> entities)
        {
            Collection.InsertMany(entities);
        }

        public void Update(FilterDefinition<T> fiter, UpdateDefinition<T> update)
        {
            Collection.Update(fiter, update);
        }

        public void Update(T args)
        {
            try
            {
                var fiter = Builders<T>.Filter.Eq("Id", args.Id);
                IList<UpdateDefinition<T>> update = new List<UpdateDefinition<T>>();
                foreach (PropertyInfo p in args.GetType().GetProperties())
                {
                    if (!p.Name.Equals("Id"))
                        update.Add(Builders<T>.Update.Set(p.Name, p.GetValue(args)));
                }
                Collection.Update(fiter, Builders<T>.Update.Combine(update));
            }
            catch {

            }
        }

        public virtual void Delete(QueryDocument args)
        {
            Collection.Delete(args);
        }

        public virtual void DeleteById(TId args)
        {
            Collection.DeleteById(args);
        }

        public virtual IQueryable<T> AsQueryable()
        {
            return Collection.AsQueryable();
        }

        #endregion
    }

    public class MongoRepository<T> : MongoRepositoryWithTypedId<T, ObjectId>, IRepository<T> where T : Entity.Entity
    {
    }
}
